const Enums = require('../doc/enums');
const utils = require('../utils/utils');
const colCache = require('../utils/col-cache');
const _ = require('lodash');
const CellTypes = Enums.ValueType;

/**
 * Cell模板。除了cell value，其他静态内容预先渲染
 */
class CellTemplate {
  constructor(row, col) {
    // <c r="" t="" s="">
    this.cellTag = '';
    // <v>cell value</v>
    this._value = null;

    this.address = colCache.encodeAddress(row.number, col.number);
    this.row = row;
    this.col = col;
    this.style = _.merge({}, this.col.style, this.row.style);
  }

  get value() {
    return this._value;
  }

  set value(val) {
    this._value = val;
  }

  // 设置Shared String ID
  set ssId(ssId) {
    this._value = ssId;
  }

  // 单元格类型
  get type() {
    return CellTypes.Null;
  }

  // xml中单元格类型，即标签中的t属性
  get xmlType() {
    return '';
  }

  get style() {
    return this._style;
  }

  set style(val) {
    this._style = val;
  }

  // 预渲染，把静态内容准备好
  preRender(ctx) {
    const styleId = ctx.styles.addStyleModel(this.style, this.type);
    this.cellTag = `<c r="${this.address}"${this.xmlType} s="${styleId}">`;
  }

  // 渲染cell标签
  render(ctx) {
    // 需要确保三个字段都是合法xml字符
    const { xmlStream } = ctx;
    const { value } = this;
    xmlStream.writeXml(this.cellTag);
    xmlStream.writeXml(`<v>${value === undefined ? '' : value}</v></c>`);
    this._value = null;
  }

  // 预渲染后清理字段
  clear() {
    delete this.address;
    delete this.row;
    delete this.col;
    delete this._style;
  }

  to(cellType) {
    const newCell = CellTemplate.createCell(cellType, this.row, this.col);
    newCell.value = this.value;
    newCell.style = this.style;
    return newCell;
  }

  static createCell(cellType, ...args) {
    switch (cellType) {
      case CellTypes.Null:
        return new CellTemplate(...args);
      case CellTypes.String:
        return new StringCell(...args);
      case CellTypes.Hyperlink:
        return new HyperlinkCell(...args);
      case CellTypes.Date:
        return new DateCell(...args);
      case CellTypes.Error:
        return new ErrorCell(...args);
      case CellTypes.Boolean:
        return new BoolCell(...args);
      case CellTypes.Number:
        return new NumberCell(...args);
      default:
        throw new TypeError('不支持的类型');
    }
  }
}

class StringCell extends CellTemplate {
  get value() {
    return this._value == null ? '' : utils.xmlEncode(this._value);
  }

  set value(val) {
    super.value = val;
  }

  get xmlType() {
    return ' t="s"';
  }

  get type() {
    return CellTypes.String;
  }

  render(ctx) {
    const ssId = ctx.sharedStrings.add(this._value);
    this.ssId = ssId;
    super.render(ctx);
  }
}

class HyperlinkCell extends CellTemplate {
  get type() {
    return CellTypes.Hyperlink;
  }

  get xmlType() {
    return ' t="s"';
  }

  render(ctx) {
    if (!this._value) {
      super.render(ctx);
      return;
    }

    const { text, hyperlink, tooltip } = this._value;
    const ssId = ctx.sharedString.add(text);
    this.ssId = ssId;

    if (/^[^!]+![a-zA-Z]+\d+$/.test(hyperlink)) {
      // excel内部链接
      ctx.hyperlinks.push({
        ref: this.address,
        tooltip,
        location: hyperlink
      });
    } else {
      // todo：支持外链 添加relations
      throw new Error('暂不支持外部链接');
    }

    super.render(ctx);
  }
}

// todo: 待补充
class RichTextCell extends CellTemplate {

}

class NumberCell extends CellTemplate {
  get type() {
    return CellTypes.Number;
  }

  get xmlType() {
    return ' t="n"';
  }
}

class DateCell extends CellTemplate {
  get type() {
    return CellTypes.Date;
  }

  get xmlType() {
    return ' t="d"';
  }

  get value() {
    if (this._value instanceof Date) {
      return this._value.toISOString();
    }
    return this._value;
  }

  set value(val) {
    super.value = val;
  }
}

class BoolCell extends CellTemplate {
  get type() {
    return CellTypes.Boolean;
  }

  get xmlType() {
    return ' t="b"';
  }

  get value() {
    return this._value ? '1' : '0';
  }

  set value(val) {
    super.value = val;
  }
}

class ErrorCell extends CellTemplate {
  get type() {
    return CellTypes.Error;
  }

  get xmlType() {
    return ' t="e"';
  }

  get value() {
    return this._value && this._value.error;
  }

  set value(val) {
    super.value = val;
  }
}

module.exports = CellTemplate;
